System.Span<T> 구조체
이 문서에서는 이 API에 대한 참조 설명서에 대한 추가 설명서를 제공합니다.
형식은 Span<T> 관리되는 힙이 아닌 스택에 할당된 ref 구조 체입니다. Ref 구조체 형식에는 boxed할 수 없고, 형식의 변수 또는 인터페이스 형식에 할당할 수 없고, 참조 형식 Objectdynamic
의 필드가 될 수 없으며 경계를 넘어 await
yield
사용할 수 없다는 것을 포함하여 관리되는 힙으로 승격할 수 없도록 하는 여러 제한 사항이 있습니다. 또한 두 메서드 Equals(Object) 를 호출하고 GetHashCode.NotSupportedException
Important
스택 전용 형식 Span<T>
이므로 힙의 버퍼에 대한 참조를 저장해야 하는 많은 시나리오에서는 적합하지 않습니다. 예를 들어 비동기 메서드를 호출하는 루틴의 경우도 마찬가지입니다. 이러한 시나리오의 경우 보완 System.Memory<T> 및 System.ReadOnlyMemory<T> 형식을 사용할 수 있습니다.
변경할 수 없거나 읽기 전용 구조를 System.ReadOnlySpan<T>나타내는 범위의 경우 .
메모리
A Span<T>
는 임의 메모리의 연속 영역을 나타냅니다. Span<T>
인스턴스는 배열의 요소 또는 배열의 일부를 보유하는 데 자주 사용됩니다. 그러나 배열과 달리 인스턴스는 Span<T>
스택에서 관리되는 메모리, 네이티브 메모리 또는 메모리를 가리킬 수 있습니다. 다음 예제에서는 배열에서 만듭니다 Span<Byte>
.
// Create a span over an array.
var array = new byte[100];
var arraySpan = new Span<byte>(array);
byte data = 0;
for (int ctr = 0; ctr < arraySpan.Length; ctr++)
arraySpan[ctr] = data++;
int arraySum = 0;
foreach (var value in array)
arraySum += value;
Console.WriteLine($"The sum is {arraySum}");
// Output: The sum is 4950
// Create a span over an array.
let array = Array.zeroCreate<byte> 100
let arraySpan = Span<byte> array
let mutable data = 0uy
for i = 0 to arraySpan.Length - 1 do
arraySpan[i] <- data
data <- data + 1uy
let mutable arraySum = 0
for value in array do
arraySum <- arraySum + int value
printfn $"The sum is {arraySum}"
// Output: The sum is 4950
다음 예제에서는 네이 Span<Byte>
티브 메모리의 100바이트에서 만듭니다.
// Create a span from native memory.
var native = Marshal.AllocHGlobal(100);
Span<byte> nativeSpan;
unsafe
{
nativeSpan = new Span<byte>(native.ToPointer(), 100);
}
byte data = 0;
for (int ctr = 0; ctr < nativeSpan.Length; ctr++)
nativeSpan[ctr] = data++;
int nativeSum = 0;
foreach (var value in nativeSpan)
nativeSum += value;
Console.WriteLine($"The sum is {nativeSum}");
Marshal.FreeHGlobal(native);
// Output: The sum is 4950
// Create a span from native memory.
let native = Marshal.AllocHGlobal 100
let nativeSpan = Span<byte>(native.ToPointer(), 100)
let mutable data = 0uy
for i = 0 to nativeSpan.Length - 1 do
nativeSpan[i] <- data
data <- data + 1uy
let mutable nativeSum = 0
for value in nativeSpan do
nativeSum <- nativeSum + int value
printfn $"The sum is {nativeSum}"
Marshal.FreeHGlobal native
// Output: The sum is 4950
다음 예제에서는 C# stackalloc 키워드를 사용하여 스택에 100바이트의 메모리를 할당합니다.
// Create a span on the stack.
byte data = 0;
Span<byte> stackSpan = stackalloc byte[100];
for (int ctr = 0; ctr < stackSpan.Length; ctr++)
stackSpan[ctr] = data++;
int stackSum = 0;
foreach (var value in stackSpan)
stackSum += value;
Console.WriteLine($"The sum is {stackSum}");
// Output: The sum is 4950
// Create a span on the stack.
let mutable data = 0uy
let stackSpan =
let p = NativeInterop.NativePtr.stackalloc<byte> 100 |> NativeInterop.NativePtr.toVoidPtr
Span<byte>(p, 100)
for i = 0 to stackSpan.Length - 1 do
stackSpan[i] <- data
data <- data + 1uy
let mutable stackSum = 0
for value in stackSpan do
stackSum <- stackSum + int value
printfn $"The sum is {stackSum}"
// Output: The sum is 4950
Span<T>
임의의 메모리 블록에 대한 추상화이므로 형식의 Span<T>
메서드와 매개 변수가 포함된 Span<T>
메서드는 캡슐화된 메모리 종류에 관계없이 모든 Span<T>
개체에서 작동합니다. 예를 들어, 다음 예제와 같이 범위를 초기화하고 해당 요소의 합계를 계산하는 코드의 각 개별 섹션을 단일 초기화 및 계산 메서드로 변경할 수 있습니다.
public static void WorkWithSpans()
{
// Create a span over an array.
var array = new byte[100];
var arraySpan = new Span<byte>(array);
InitializeSpan(arraySpan);
Console.WriteLine($"The sum is {ComputeSum(arraySpan):N0}");
// Create an array from native memory.
var native = Marshal.AllocHGlobal(100);
Span<byte> nativeSpan;
unsafe
{
nativeSpan = new Span<byte>(native.ToPointer(), 100);
}
InitializeSpan(nativeSpan);
Console.WriteLine($"The sum is {ComputeSum(nativeSpan):N0}");
Marshal.FreeHGlobal(native);
// Create a span on the stack.
Span<byte> stackSpan = stackalloc byte[100];
InitializeSpan(stackSpan);
Console.WriteLine($"The sum is {ComputeSum(stackSpan):N0}");
}
public static void InitializeSpan(Span<byte> span)
{
byte value = 0;
for (int ctr = 0; ctr < span.Length; ctr++)
span[ctr] = value++;
}
public static int ComputeSum(Span<byte> span)
{
int sum = 0;
foreach (var value in span)
sum += value;
return sum;
}
// The example displays the following output:
// The sum is 4,950
// The sum is 4,950
// The sum is 4,950
open System
open System.Runtime.InteropServices
open FSharp.NativeInterop
// Package FSharp.NativeInterop.NativePtr.stackalloc for reuse.
let inline stackalloc<'a when 'a: unmanaged> length : Span<'a> =
let voidPointer = NativePtr.stackalloc<'a> length |> NativePtr.toVoidPtr
Span<'a>(voidPointer, length)
let initializeSpan (span: Span<byte>) =
let mutable value = 0uy
for i = 0 to span.Length - 1 do
span[i] <- value
value <- value + 1uy
let computeSum (span: Span<byte>) =
let mutable sum = 0
for value in span do
sum <- sum + int value
sum
let workWithSpans () =
// Create a span over an array.
let array = Array.zeroCreate<byte> 100
let arraySpan = Span<byte> array
initializeSpan arraySpan
printfn $"The sum is {computeSum arraySpan:N0}"
// Create an array from native memory.
let native = Marshal.AllocHGlobal 100
let nativeSpan = Span<byte>(native.ToPointer(), 100)
initializeSpan nativeSpan
printfn $"The sum is {computeSum nativeSpan:N0}"
Marshal.FreeHGlobal native
// Create a span on the stack.
let stackSpan = stackalloc 100
initializeSpan stackSpan
printfn $"The sum is {computeSum stackSpan:N0}"
// The example displays the following output:
// The sum is 4,950
// The sum is 4,950
// The sum is 4,950
배열
배열 Span<T>
을 래핑할 때 메모리 섹션의 예제에서와 같이 전체 배열을 래핑할 수 있습니다. 조각화 Span<T>
가 지원되므로 배열 내의 인접 범위를 가리킬 수도 있습니다.
다음 예제에서는 10개 요소 정수 배열의 중간 5개 요소 조각을 만듭니다. 코드는 조각에 있는 각 정수의 값을 두 배로 둡니다. 출력에서 보여 주듯이 범위에 의해 변경된 내용은 배열 값에 반영됩니다.
using System;
var array = new int[] { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 };
var slice = new Span<int>(array, 2, 5);
for (int ctr = 0; ctr < slice.Length; ctr++)
slice[ctr] *= 2;
// Examine the original array values.
foreach (var value in array)
Console.Write($"{value} ");
Console.WriteLine();
// The example displays the following output:
// 2 4 12 16 20 24 28 16 18 20
module Program
open System
[<EntryPoint>]
let main _ =
let array = [| 2; 4; 6; 8; 10; 12; 14; 16; 18; 20 |]
let slice = Span<int>(array, 2, 5)
for i = 0 to slice.Length - 1 do
slice[i] <- slice[i] * 2
// Examine the original array values.
for value in array do
printf $"{value} "
printfn ""
0
// The example displays the following output:
// 2 4 12 16 20 24 28 16 18 20
조각
Span<T>
에는 지정된 인덱스에서 Slice 시작하는 현재 범위에서 조각을 형성하는 메서드의 두 오버로드가 포함됩니다. 이렇게 하면 데이터를 최소한의 성능 영향으로 데이터 처리 파이프라인의 일부에서 Span<T>
필요에 따라 처리할 수 있는 논리 청크 집합으로 처리할 수 있습니다. 예를 들어 최신 서버 프로토콜은 종종 텍스트 기반이므로 문자열 및 부분 문자열을 조작하는 것이 특히 중요합니다. String 클래스에서 부분 문자열을 추출하는 주요 메서드는 .입니다Substring. 광범위한 문자열 조작을 사용하는 데이터 파이프라인의 경우 다음과 같은 이유로 성능이 저하됩니다.
- 부분 문자열을 저장할 새 문자열을 만듭니다.
- 원래 문자열에서 새 문자열로 문자의 하위 집합을 복사합니다.
이 할당 및 복사 작업은 다음 예제와 같이 사용 Span<T>
하거나 ReadOnlySpan<T>사용하여 제거할 수 있습니다.
using System;
class Program2
{
static void Run()
{
string contentLength = "Content-Length: 132";
var length = GetContentLength(contentLength.ToCharArray());
Console.WriteLine($"Content length: {length}");
}
private static int GetContentLength(ReadOnlySpan<char> span)
{
var slice = span.Slice(16);
return int.Parse(slice);
}
}
// Output:
// Content length: 132
module Program2
open System
let getContentLength (span: ReadOnlySpan<char>) =
let slice = span.Slice 16
Int32.Parse slice
let contentLength = "Content-Length: 132"
let length = getContentLength (contentLength.ToCharArray())
printfn $"Content length: {length}"
// Output:
// Content length: 132
.NET